/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-13
 * V4.0
 */
package com.jphenix.webserver.servlet;

import com.jphenix.share.util.StreamUtil;
import com.jphenix.standard.docs.ClassInfo;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * CGIServlet 无用
 * @author 刘虻
 * 2006-9-15上午12:10:31
 */
@ClassInfo({"2014-06-13 18:58","CGIServlet"})
public class CgiServlet extends HttpServlet {

	/**
	 * 版本标识
	 */
	private static final long serialVersionUID = 2903987189939315032L;

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-9上午08:20:47
	 */
	@Override
    public String getServletInfo() {
		return "runs CGI programs";
	}
	
	
	/**
	 * 获取类实例
	 * @author 刘虻
	 * 2008-6-28下午04:35:07
	 * @return 类实例
	 */
	public static HttpServlet newInstance() {
		return new CgiServlet();
	}


	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-9上午08:21:06
	 */
	@Override
    public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		if (!("get".equalsIgnoreCase(req.getMethod()) || "post".equalsIgnoreCase(req.getMethod()))) {
			res.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
			return;
		}
		dispatchPathname(req, res, getServletContext().getRealPath(req.getServletPath() + req.getPathInfo()));
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-9上午08:22:22
	 * @param req
	 * @param res
	 * @param path
	 * @throws IOException
	 */
	protected void dispatchPathname(HttpServletRequest req, HttpServletResponse res, String path) throws IOException {
		if (new File(path).exists()) {
            serveFile(req, res, path);
        } else {
            res.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-9上午08:22:28
	 * @param req
	 * @param res
	 * @param path
	 * @throws IOException
	 * @deprecated
	 */
    protected void serveFile(HttpServletRequest req, HttpServletResponse res, String path) throws IOException {
		String queryString = req.getQueryString();
		int contentLength = req.getContentLength();
		int c;

		log("running " + path + "?" + queryString);

		// Make argument list.
		String[] argList = (path + (queryString != null && queryString.indexOf("=") == -1 ? "+" + queryString : ""))
				.split("\\+");

		// Make environment list.
		Vector<String> envVec = new Vector<String>();
		envVec.addElement(makeEnv("PATH", "/usr/local/bin:/usr/ucb:/bin:/usr/bin"));
		envVec.addElement(makeEnv("GATEWAY_INTERFACE", "CGI/1.1"));
		envVec.addElement(makeEnv("SERVER_SOFTWARE", getServletContext().getServerInfo()));
		envVec.addElement(makeEnv("SERVER_NAME", req.getServerName()));
		envVec.addElement(makeEnv("SERVER_PORT", Integer.toString(req.getServerPort())));
		envVec.addElement(makeEnv("REMOTE_ADDR", req.getRemoteAddr()));
		envVec.addElement(makeEnv("REMOTE_HOST", req.getRemoteHost()));
		envVec.addElement(makeEnv("REQUEST_METHOD", req.getMethod()));
		if (contentLength != -1) {
            envVec.addElement(makeEnv("CONTENT_LENGTH", Integer.toString(contentLength)));
        }
		if (req.getContentType() != null) {
            envVec.addElement(makeEnv("CONTENT_TYPE", req.getContentType()));
        }
		envVec.addElement(makeEnv("SCRIPT_NAME", req.getServletPath()));
		if (req.getPathInfo() != null) {
            envVec.addElement(makeEnv("PATH_INFO", req.getPathInfo()));
        }
		if (req.getPathTranslated() != null) {
            envVec.addElement(makeEnv("PATH_TRANSLATED", req.getPathTranslated()));
        }
		if (queryString != null) {
            envVec.addElement(makeEnv("QUERY_STRING", queryString));
        }
		envVec.addElement(makeEnv("SERVER_PROTOCOL", req.getProtocol()));
		if (req.getRemoteUser() != null) {
            envVec.addElement(makeEnv("REMOTE_USER", req.getRemoteUser()));
        }
		if (req.getAuthType() != null) {
            envVec.addElement(makeEnv("AUTH_TYPE", req.getAuthType()));
        }
		Enumeration<String> hnEnum = req.getHeaderNames();
		while (hnEnum.hasMoreElements()) {
			String name =  hnEnum.nextElement();
			String value = req.getHeader(name);
			if (value == null) {
                value = "";
            }
			envVec.addElement(makeEnv("HTTP_" + name.toUpperCase().replace('-', '_'), value));
		}
		String[] envList = makeList(envVec);

		// Start the command.
		Process proc = Runtime.getRuntime().exec(argList, envList);

		try {
			// If it's a POST, copy the request data to the process.
			if ("post".equalsIgnoreCase(req.getMethod())) {
				InputStream reqIn = req.getInputStream();
				OutputStream procOut = proc.getOutputStream();
				for (int i = 0; i < contentLength; ++i) {
					c = reqIn.read();
					if (c == -1) {
                        break;
                    }
					procOut.write(c);
				}
				procOut.close();
			}

			// Now read the response from the process.
			BufferedReader procIn = new BufferedReader(new InputStreamReader(proc.getInputStream()));
			OutputStream resOut = res.getOutputStream();
			// Some of the headers have to be intercepted and handled.
			boolean firstLine = true;
			while (true) {
				String line = procIn.readLine();
				if (line == null) {
                    break;
                }
				line = line.trim();
				if (line.length()==0) {
                    break;
                }
				int colon = line.indexOf(":");
				if (colon == -1) {
					// No colon. If it's the first line, parse it for status.
					if (firstLine) {
						StringTokenizer tok = new StringTokenizer(line, " ");
						try {
							switch (tok.countTokens()) {
							case 2:
								tok.nextToken();
								res.setStatus(Integer.parseInt(tok.nextToken()));
								break;
							case 3:
								tok.nextToken();
								res.setStatus(Integer.parseInt(tok.nextToken()), tok.nextToken());
								break;
							}
						} catch (NumberFormatException ignore) {
						}
					} else {
						// No colon and it's not the first line? Ignore.
					}
				} else {
					// There's a colon. Check for certain special headers.
					String name = line.substring(0, colon);
					String value = line.substring(colon + 1).trim();
					if ("Status".equalsIgnoreCase(name)) {
						StringTokenizer tok = new StringTokenizer(value, " ");
						try {
							switch (tok.countTokens()) {
							case 1:
								res.setStatus(Integer.parseInt(tok.nextToken()));
								break;
							case 2:
								res.setStatus(Integer.parseInt(tok.nextToken()), tok.nextToken());
								break;
							}
						} catch (NumberFormatException ignore) {
						}
					} else if ("Content-type".equalsIgnoreCase(name)) {
						res.setContentType(value);
					} else if ("Content-length".equalsIgnoreCase(name)) {
						try {
							res.setContentLength(Integer.parseInt(value));
						} catch (NumberFormatException ignore) {
						}
					} else if ("Location".equalsIgnoreCase(name)) {
						res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
						res.setHeader(name, value);
					} else {
						// Not a special header. Just set it.
						res.setHeader(name, value);
					}
				}
			}
			// Copy the rest of the data uninterpreted.
			StreamUtil.copyStream(procIn, resOut, null);
			procIn.close();
			resOut.close();
		} catch (IOException e) {
			// res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
			// There's some weird bug in Java, when reading from a Process
			// you get a spurious IOException. We have to ignore it.
		}
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-9上午08:22:39
	 * @param name
	 * @param value
	 * @return
	 */
	protected static String makeEnv(String name, String value) {
		return name + "=" + value;
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-9上午08:22:44
	 * @param vec
	 * @return
	 */
	protected static String[] makeList(Vector<String> vec) {
		String[] list = new String[vec.size()];
		for (int i = 0; i < vec.size(); ++i) {
			list[i] = vec.elementAt(i);
		}
		return list;
	}

}
